package cn.yzw.jc.sandbox.spring.listener;

import cn.hutool.core.collection.CollectionUtil;
import cn.yzw.jc.sandbox.spring.domain.SpringBeanModel;
import com.alibaba.jvm.sandbox.api.event.BeforeEvent;
import com.alibaba.jvm.sandbox.api.event.Event;
import com.alibaba.jvm.sandbox.api.event.ReturnEvent;
import com.alibaba.jvm.sandbox.api.listener.EventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * bean初始化监听类
 *
 * @author liucan@yzw.cn write on 2024/1/8 15:17
 */
public class SpringBeanInitListener implements EventListener {
    private final static Logger log = LoggerFactory.getLogger(SpringBeanInitListener.class);
    /**
     * application 启动状态
     */
    private final AtomicBoolean applicationStatus = new AtomicBoolean(false);
    /**
     * 缓存bean 初始化信息
     */
    private static final Map<String, SpringBeanModel> SPRING_BEAN_MAP = new ConcurrentHashMap<>();

    private final static String SPLITTER = "-";
    private final static List<String> SPRING_START_EVENT_ID_LIST = new ArrayList<>();

    @Override
    public void onEvent(Event event) {
        if (event instanceof BeforeEvent) {
            BeforeEvent beforeEvent = (BeforeEvent) event;
            String uniqueKey = beforeEvent.invokeId + SPLITTER + beforeEvent.processId;

            boolean isInitBean = "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory".equals(beforeEvent.javaClassName);

            if (isInitBean) {
                beforeEventWithSpringBean(beforeEvent, uniqueKey);
            } else {
                SPRING_START_EVENT_ID_LIST.add(uniqueKey);
                log.info("monitor spring application is running,uniqueKey {}", uniqueKey);
            }
        } else if (event instanceof ReturnEvent) {
            ReturnEvent returnEvent = (ReturnEvent) event;
            String uniqueKey = returnEvent.invokeId + SPLITTER + returnEvent.processId;

            if (CollectionUtil.isNotEmpty(SPRING_START_EVENT_ID_LIST) && SPRING_START_EVENT_ID_LIST.get(0).equals(uniqueKey)) {
                applicationStatus.set(true);
                log.info("monitor spring application has started");
            } else {
                returnEventWithSpringBean(returnEvent, uniqueKey);
            }
        }
    }

    private void beforeEventWithSpringBean(BeforeEvent beforeEvent, String uniqueKey) {
        if (SPRING_BEAN_MAP.containsKey(uniqueKey)) {
            log.error("springBeanMap contains key error: " + uniqueKey);
            return;
        }
        SpringBeanModel springBean = new SpringBeanModel();
        springBean.setBeanName((beforeEvent).argumentArray[0].toString());
        springBean.setClassName(Optional.of(beforeEvent.argumentArray[1])
                .map(Object::getClass)
                .map(Class::toString)
                .orElse(null));
//        springBean.setClassLoaderName(Optional.of(beforeEvent.argumentArray[1]).map(Object::getClass)
//                .map(Class::getClassLoader)
//                .map(ClassLoader::getClass)
//                .map(Class::toString)
//                .orElse(null));
        springBean.startTimer();
        SPRING_BEAN_MAP.put(uniqueKey, springBean);
    }

    private void returnEventWithSpringBean(ReturnEvent returnEvent, String uniqueKey) {
        if (!SPRING_BEAN_MAP.containsKey(uniqueKey)) {
            log.error("springBeanMap do not contains key error: " + uniqueKey);
            return;
        }
        SpringBeanModel springBean = SPRING_BEAN_MAP.get(uniqueKey);
        springBean.finishTimer();
    }

    public Map<String, SpringBeanModel> getSpringBeanMap() {
        return SPRING_BEAN_MAP;
    }

    public Boolean getApplicationStartStatus() {
        return applicationStatus.get();
    }
}
